// 内蒙古工业大学教务系统课程导入脚本
// 根据教务处网站内容解析课程表数据
// 2025.11.25

// ============生成时间段配置==============

/**
 * 默认时段配置，来源于学校官网(2025.11.23)
 */
const defaultTimeSlots = [
    { "number": 1, "startTime": "08:00", "endTime": "08:45" },
    { "number": 2, "startTime": "08:55", "endTime": "09:35" },
    { "number": 3, "startTime": "10:05", "endTime": "10:50" },
    { "number": 4, "startTime": "11:00", "endTime": "11:40" },
    { "number": 5, "startTime": "13:30", "endTime": "14:15" },
    { "number": 6, "startTime": "14:25", "endTime": "15:05" },
    { "number": 7, "startTime": "15:35", "endTime": "16:20" },
    { "number": 8, "startTime": "16:30", "endTime": "17:10" },
    { "number": 9, "startTime": "18:00", "endTime": "18:45" },
    { "number": 10, "startTime": "18:45", "endTime": "19:35" },
    { "number": 11, "startTime": "19:45", "endTime": "20:30" },
    { "number": 12, "startTime": "20:30", "endTime": "21:20" }
];

/**
 * 从HTML文本中解析时段信息
 * @param {string} doc -  DOM 文档对象
 * @returns {Object} 时段信息对象
 */
function parseTimeSlotsFromHTML(doc) {
    const timeSlots = {};
    
    const timetable = doc.querySelector('table#timetable');
    if (timetable) {
        const rows = timetable.querySelectorAll('tr');
        
        for (let i = 1; i < rows.length; i++) { // 跳过表头行
            const th = rows[i].querySelector('th');
            if (th) {
                const sectionText = th.textContent.trim();
                
                // 解析格式如："第1节\n08:20\n┆\n09:05"
                const sectionMatch = sectionText.match(/第(\d+)节/);
                const timeMatch = sectionText.match(/(\d{2}:\d{2})/g);
                
                if (sectionMatch && timeMatch && timeMatch.length >= 2) {
                    const section = parseInt(sectionMatch[1]);
                    timeSlots[section] = {
                        section: section,
                        startTime: timeMatch[0],
                        endTime: timeMatch[1]
                    };
                }
            }
        }
    }
    
    if (Object.keys(timeSlots).length === 0) {
        throw new Error('未找到时段信息表格');
    }
    
    return timeSlots;
}

/**
 * 从指定网页地址异步获取HTML并解析时段信息，如果解析失败则返回默认时段
 * @param {string} url - 网页地址
 * @returns {Promise<Array<Object>>} 时段信息数组，按节次排序
 * @returns {number} .number 节次编号
 * @returns {string} .startTime 开始时间
 * @returns {string} .endTime 结束时间
 */
async function getTimeSlotsArray(url) {
    try {
        const doc = await fetchAndParseHTML(url, 'gbk');
        
        // 解析时段信息
        const timeSlots = parseTimeSlotsFromHTML(doc);
        
        const hasValidData = Object.keys(timeSlots).length > 0 && 
                            timeSlots[1] && timeSlots[1].startTime;
        
        if (hasValidData) {
            // 转换为目标格式
            return Object.values(timeSlots).map(slot => ({
                number: slot.section,
                startTime: slot.startTime,
                endTime: slot.endTime
            })).sort((a, b) => a.number - b.number);
        } else {
            throw new Error('解析到的时段数据不完整');
        }
    } catch (error) {
        console.error('从HTML解析时段信息失败，使用默认时段:', error.message);
        // 使用默认时段
        return defaultTimeSlots;
    }
}

// ============解析课程表数据==============

/**
 * 解析周数字符串
 * @param {string} weeksText - 周数字符串，支持格式："11周"、"1-13周"、"1-10周,11-18周"
 * @returns {number[]} 解析后的周数数组，按升序排列
 */
function parseWeeks(weeksText) {
    if (!weeksText) return [];
    
    const weeks = [];
    const text = weeksText.replace('周', '').trim();
    
    // 处理单个周数 "11周" -> [11]
    if (/^\d+$/.test(text)) {
        return [parseInt(text)];
    }
    
    // 处理范围 "1-13周" -> [1,2,3,...,13]
    const rangeMatch = text.match(/^(\d+)-(\d+)$/);
    if (rangeMatch) {
        const start = parseInt(rangeMatch[1]);
        const end = parseInt(rangeMatch[2]);
        for (let i = start; i <= end; i++) {
            weeks.push(i);
        }
        return weeks;
    }
    
    // 处理多个范围 "1-10周,11-18周"
    const ranges = text.split(',');
    ranges.forEach(range => {
        const singleMatch = range.match(/^(\d+)$/);
        if (singleMatch) {
            weeks.push(parseInt(singleMatch[1]));
        } else {
            const rangeMatch = range.match(/(\d+)-(\d+)/);
            if (rangeMatch) {
                const start = parseInt(rangeMatch[1]);
                const end = parseInt(rangeMatch[2]);
                for (let i = start; i <= end; i++) {
                    weeks.push(i);
                }
            }
        }
    });
    
    return weeks;
}

/**
 * 解析课程名称（去除<<>>）
 * @param {*} courseText - 原始课程名称文本
 * @returns {string} 解析后的课程名称
 */
function parseCourseName(courseText) {
    let name = courseText
        .replace(/<</g, '')  // 直接移除 <<
        .replace(/>>/g, '')  // 直接移除 >>
        .split(';')[0];
    
    return name.trim();
}

function parseSingleCourse(lines, day, timeSlot) {
    
    const courseNameMatch = lines[0].match(/<<(.*?)>>/);

    if (!courseNameMatch) {
        return null;
    }

    let courseData = {
        name: parseCourseName(courseNameMatch[1]),
        position: lines[1] || '',
        day: day,
        startSection: timeSlot,
        endSection: timeSlot,
        weeks: []
    };

    // 单门课程示例
    // ['<<离散数学导论>>;1', '教C', '贾老师', '1-15周', '讲课']
    // 无教师名课程示例：
    // ['<<体育选项课(一)>>;11', '操   场', '2-18周', '讲课']

    if (lines.length > 4) {
        // 有教师名课程
        courseData.teacher = lines[2].replace(/,$/, '');
        courseData.weeks = parseWeeks(lines[3]);
    } else {
        // 无教师名课程
        courseData.teacher = '';
        courseData.weeks = parseWeeks(lines[2]);
    }

    return courseData;
}

/**
 * 解析包含多个课程的课程信息块。
 *
 * @param {Array<string>} lines - 包含课程信息的字符串数组，每个元素表示一行数据。
 * @param {string} day - 表示课程所在的星期几。
 * @param {string} timeSlot - 表示课程所在的时间段。
 * @returns {Array<Object>} 返回一个包含课程信息的数组，每个课程信息是一个对象。
 */
function parseMultipleCourses(lines, day, timeSlot) {
    const courses = [];
    let currentCourseLines = [];

    // 示例：
    // ['<<工程训练C>>;11', '格物楼D', '刘老师', '1-10周', '讲课', '<<数据结构与算法>>;1', '教C', '秦老师', '11-18周', '讲课']

    for (let i = 0; i < lines.length; i++) {
        if (lines[i].includes('<<') && currentCourseLines.length > 0) {
            const courseData = parseSingleCourse(currentCourseLines, day, timeSlot);
            if (courseData) {
                courses.push(courseData);
            }
            currentCourseLines = [];
        }
        currentCourseLines.push(lines[i]);
    }

    if (currentCourseLines.length > 0) {

        const courseData = parseSingleCourse(currentCourseLines, day, timeSlot);
        if (courseData) {
            courses.push(courseData);
        }
    }

    return courses;
}

/**
 * 处理课程区块信息，解析出课程的详细信息。
 *
 * @param {string} block - 包含课程信息的HTML字符串，使用`<br>`分隔每行。
 * @param {string} day - 表示课程所在的星期几。
 * @param {string} timeSlot - 表示课程所在的时间段。
 * @returns {Array<Object>} 返回一个包含课程信息的数组，每个课程信息是一个对象。
 */
function processCourseBlock(block, day, timeSlot) {
    const lines = block.split('<br>').map(line => 
        line.replace(/&lt;/g, '<').replace(/&gt;/g, '>').trim()
    ).filter(line => line);

    const courses = [];

    const courseCount = lines.filter(line => line.includes('<<')).length;

    if (courseCount > 1) {
        courses.push(...parseMultipleCourses(lines, day, timeSlot));
    } else if (lines.length >= 4) {
        const courseData = parseSingleCourse(lines, day, timeSlot);
        if (courseData) {
            courses.push(courseData);
        }
    }

    return courses;
}

/**
 * 将HTML课程表转换为标准格式的课程数据
 * @param {string} url - 网页地址
 * @returns {Promise<Array<Object>>} 课程表数据数组
 * @returns {string} .name 课程名称
 * @returns {string} .teacher 授课教师
 * @returns {string} .position 上课地点
 * @returns {number} .day 星期几 (1=周一, 7=周日)
 * @returns {number} .startSection 开始节次
 * @returns {number} .endSection 结束节次
 * @returns {number[]} .weeks 上课周次数组
 */
async function convertToTargetFormat(url) {
    try {
        const doc = await fetchAndParseHTML(url, 'gbk');
        
        const timetable = [];
        const rows = doc.querySelectorAll('#timetable tr');
        
        // 跳过表头行
        for (let rowIndex = 1; rowIndex < rows.length; rowIndex++) {
            const row = rows[rowIndex];
            const timeSlot = rowIndex; // 第1-13节对应rowIndex 1-13
            const cells = row.querySelectorAll('td');
            
            for (let day = 1; day <= cells.length; day++) {
                const cell = cells[day - 1];
                const content = cell.innerHTML.trim();
                
                if (content && content !== '&nbsp;') {

                    // 分割每个课程块（一个单元格可能有多个课程）
                    const courseBlocks = content.split(/<br>\s*<br>/);
                    
                    courseBlocks.forEach(block => {
                        if (block.trim()) {

                            const courses = processCourseBlock(block, day, timeSlot);

                            for (const course of courses) {
                                timetable.push(course);

                            }
                        }
                    });
                }
            }
        }

        return timetable;
        
    } catch (error) {

        return []; // 返回空数组作为错误回退
    }
}

/**
 * 合并连续的课程信息。
 * 合并条件：同一天、同一课程名称、同一位置、同一教师、同一周次且时间连续
 *
 * @param {Array<Object>} courses - 课程信息数组
 * @returns {Array<Object>} 返回合并后的课程信息数组
 */
function mergeContinuousCourses(courses) {
    // 按所有关键属性进行分组
    const grouped = {};
    
    courses.forEach(course => {
        // 使用周次数组的字符串表示作为分组键的一部分
        const weeksKey = JSON.stringify(course.weeks.sort((a, b) => a - b));
        const key = `${course.day}-${course.name}-${course.position}-${course.teacher || '未知'}-${weeksKey}`;
        if (!grouped[key]) {
            grouped[key] = [];
        }
        grouped[key].push(course);
    });
    
    const result = [];
    
    // 处理每个分组
    Object.values(grouped).forEach(group => {
        // 按开始节次排序
        group.sort((a, b) => a.startSection - b.startSection);
        
        let currentCourse = null;
        
        group.forEach(course => {
            if (!currentCourse) {
                // 第一个课程
                currentCourse = { ...course };
            } else if (currentCourse.endSection + 1 === course.startSection) {
                // 时间连续，合并
                currentCourse.endSection = course.endSection;
            } else {
                // 时间不连续，将当前课程加入结果，开始新的课程
                result.push(currentCourse);
                currentCourse = { ...course };
            }
        });
        
        // 将最后一个课程加入结果
        if (currentCourse) {
            result.push(currentCourse);
        }
    });
    
    return result;
}

// ============配置获取==============

/*
    * 异步获取学年学期信息
    * @returns {Promise<Object>} 包含 studentid, year, term的对象
    * studentid: 标识ID
    * year: 学年，例如 45 (2025-1980)
    * term: 学期，1=春季，2=夏季，3=秋季
    */
async function getSemesterInfo(url) {
    try {
        const doc = await fetchAndParseHTML(url, 'gbk');
        
        // 查找 CTRT 元素
        const ctrtElement = doc.querySelector('eduaffair\\:CTRT');
        
        if (!ctrtElement) {
            throw new Error('未找到 CTRT 元素');
        }
        
        // 提取参数
        const params = {
            studentid: ctrtElement.getAttribute('studentid'),
            year: ctrtElement.getAttribute('year'),
            term: ctrtElement.getAttribute('term'),
        };
        
        return params;
        
    } catch (error) {
        console.error('提取参数时出错:', error);
        return null;
    }
}

/**
 * 获取指定学年和学期的最大周数值。
 *
 * @param {string} yearid - 学年的ID，例如 "2023"。
 * @param {string} termid - 学期的ID，例如 "1" 或 "2"。
 * @returns {Promise<number>} 返回一个Promise，解析为最大周数值。
 */
async function getMaxWeekValue(yearid, termid) {
    const url = `http://jw.imut.edu.cn/academic/manager/coursearrange/studentWeeklyTimetable.do?yearid=${yearid}&termid=${termid}`;
    try {
        const doc = await fetchAndParseHTML(url, 'gbk');
        
        // 查找whichWeek选择框
        const weekSelect = doc.querySelector('select[name="whichWeek"]');
        
        if (!weekSelect) {
            throw new Error('未找到周次选择框');
        }

        // 获取所有option的value并转换为数字
        const weekOptions = Array.from(weekSelect.querySelectorAll('option'));
        const weekValues = weekOptions
            .map(option => parseInt(option.value))
            .filter(value => !isNaN(value) && value !== 0); // 过滤掉非数字和空值

        if (weekValues.length === 0) {
            throw new Error('未找到有效的周数值');
        }
        const maxWeek = Math.max(...weekValues);

        return maxWeek;
        
    } catch (error) {
        console.error('获取最大周数时出错:', error);
        throw error;
    }
}

/*    * 异步获取第一个课程日期
    * @param {string} yearid - 学年ID
    * @param {string} termid - 学期ID
    * @returns {Promise<string>} 第一个课程日期字符串，格式如 "2025-09-01"
**/
async function getFirstCourseDate(yearid, termid) {
    const url = `http://jw.imut.edu.cn/academic/manager/coursearrange/studentWeeklyTimetable.do?yearid=${yearid}&termid=${termid}&whichWeek=1`;
    try {
        const doc = await fetchAndParseHTML(url, 'gbk');
        
        // 查找第一个课程日期
        const firstDateTd = doc.querySelector('td[name="td0"]');
        
        if (firstDateTd) {
            const firstCourseDate = firstDateTd.textContent.trim();
            return firstCourseDate;
        } else {
            return null;
        }
        
    } catch (error) {
        console.error('获取数据失败:', error);
        return null;
    }
}

// ====================== 辅助函数 ======================

// 请求与解析HTML的通用函数
async function fetchAndParseHTML(url, encoding = 'gbk') {
    const response = await fetch(url);
    if (!response.ok) {
        throw new Error(`HTTP错误! 状态码: ${response.status}`);
    }
    const buffer = await response.arrayBuffer();
    const decoder = new TextDecoder(encoding);
    const htmlText = decoder.decode(buffer);
    const parser = new DOMParser();
    return parser.parseFromString(htmlText, 'text/html');
}

// 日期格式验证函数
function validateDateFormat(dateString) {
    const regex = /^\d{4}-\d{2}-\d{2}$/;
    if (regex.test(dateString)) {
        return false;
    } else {
        return "请输入正确的日期格式，示例：2025-09-01";
    }
}

// 弹出日期确认对话框
async function setStartDate(suggestedDate) {
    const dateSelection = await window.AndroidBridgePromise.showPrompt(
        "请确认学期起始日期",
        `此日期来自您本学期第一节课日期，如有误，请修改（格式：YYYY-MM-DD）：`,
        suggestedDate || "",
        "validateDateFormat"
    );
    return dateSelection;
}

// ====================== 导入课程主流程 ======================

async function runImportFlow() {

    AndroidBridge.showToast("即将开始导入课表，请稍候...");

    // 获取学年学期信息
    const semesterInfo = await getSemesterInfo("http://jw.imut.edu.cn/academic/student/currcourse/currcourse.jsdo");
    if (!semesterInfo) {
        AndroidBridge.showToast("获取学生信息失败，请重试！");
        return;
    }
    currentYear = semesterInfo.year; // 当前年份 - 1980
    currentTerm = semesterInfo.term; // 当前学期

    // 构造课程表URL
    const timetableUrl = `http://jw.imut.edu.cn/academic/manager/coursearrange/showTimetable.do?id=${semesterInfo.studentid}&yearid=${semesterInfo.year}&termid=${semesterInfo.term}&timetableType=STUDENT&sectionType=BASE`;

    // 获取时段数据
    const timeSlots = await getTimeSlotsArray(timetableUrl);
    if (!timeSlots || timeSlots.length === 0) {
        AndroidBridge.showToast("获取时间段信息失败，使用默认时间段！");
    }

    // 获取并转换课程表数据
    let courses = await convertToTargetFormat(timetableUrl);
    if (courses.length === 0) {
        AndroidBridge.showToast("获取课程表数据失败，请重试！");
        return;
    }

    // 合并连续课程
    courses = mergeContinuousCourses(courses)

    // 获取第一个课程日期
    let firstCourseDate = null;
    try {
        firstCourseDate = await getFirstCourseDate(semesterInfo.year, semesterInfo.term);
    } catch (err) {
        console.warn("获取第一个课程日期失败:", err);
    }

    // 用户确认起始日期
    try {
        firstCourseDate = await setStartDate(firstCourseDate);
    } catch (err) {
        console.error("用户取消了日期输入:", err);
        AndroidBridge.showToast("未输入起始日期。");
    }

    // 获取最大周数
    let maxWeeks = 20; // 默认最大周数
    try {
        maxWeeks = await getMaxWeekValue(semesterInfo.year, semesterInfo.term);
    } catch (err) {
        console.warn("获取最大周数失败，使用默认值 20");
    }

    // 配置课表配置
    const coursesConfig = {
        semesterStartDate: firstCourseDate,
        semesterTotalWeeks: maxWeeks,
    };

    // 将数据传递给Android端

    // 提交课程数据
    try {
        await window.AndroidBridgePromise.saveImportedCourses(JSON.stringify(courses));
        const coursesCount = courses.length;
        AndroidBridge.showToast(`课程导入成功，共导入 ${coursesCount} 门课程！`);
    } catch (err) {
        console.error("课程导入失败:", err);
        AndroidBridge.showToast("课程导入失败：" + err.message);
        return;
    }

    // 提交时间段数据
    try {
        await window.AndroidBridgePromise.savePresetTimeSlots(JSON.stringify(timeSlots));
        AndroidBridge.showToast("时间段导入成功！");
    } catch (err) {
        console.error("时间段导入失败:", err);
        AndroidBridge.showToast("时间段导入失败：" + err.message);
        return;
    }

    // 提交课表配置
    try {
        await window.AndroidBridgePromise.saveCourseConfig(JSON.stringify(coursesConfig));
        AndroidBridge.showToast("课表配置保存成功！");
    } catch (err) {
        console.error("课表配置保存失败:", err);
        AndroidBridge.showToast("课表配置保存失败：" + err.message);
        return;
    }

    // 通知任务完成
    console.log("JS：整个导入流程执行完毕并成功。");
    AndroidBridge.notifyTaskCompletion();

}

runImportFlow();